package h3

import (
	"bytes"
	"fmt"
	"github.com/lucas-clemente/quic-go"
	"github.com/lucas-clemente/quic-go/quicvarint"
	"github.com/marten-seemann/qpack"
	"io"
	"net/http"
	"strconv"
	"strings"
)

// HeadersFrame
//  HEADERS Frame {
// 	    Type (i) = 0x1,
// 	    Length (i),
// 	    Encoded Field Section (..),
//  }
type HeadersFrame struct {
	Header http.Header
	// 特殊字段
	Path      string
	Authority string
	Protocol  string
}

func ParseHeadersFrame(uniStream quic.ReceiveStream) *HeadersFrame {
	reader := quicvarint.NewReader(uniStream)
	frameType, _ := quicvarint.Read(reader)
	if HeadersFrameType == frameType {
		// headers 帧
		length, _ := quicvarint.Read(reader)
		payloadBuf := make([]byte, length)
		_, _ = io.ReadFull(uniStream, payloadBuf)
		decoder := qpack.NewDecoder(nil)
		hfs, _ := decoder.DecodeFull(payloadBuf)

		httpHeaders := http.Header{}

		var path, authority, protocol string
		for _, h := range hfs {
			switch h.Name {
			case ":path":
				path = h.Value
			case ":protocol":
				protocol = h.Value
			case ":authority":
				authority = h.Value
			default:
				if !h.IsPseudo() {
					httpHeaders.Add(h.Name, h.Value)
				}
			}
		}

		// concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4
		if len(httpHeaders["Cookie"]) > 0 {
			httpHeaders.Set("Cookie", strings.Join(httpHeaders["Cookie"], "; "))
		}

		return &HeadersFrame{
			Header:    httpHeaders,
			Path:      path,
			Authority: authority,
			Protocol:  protocol,
		}
	} else {
		fmt.Printf("%d not support\n", frameType)
		SkipUnknownFrame(uniStream)
		return nil
	}
}

func (f *HeadersFrame) ToBytes(status int) []byte {
	buf := bytes.Buffer{}
	// Identifier
	quicvarint.Write(&buf, HeadersFrameType)
	// Length
	headerBuf := bytes.Buffer{}
	enc := qpack.NewEncoder(&headerBuf)
	_ = enc.WriteField(qpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)})
	for k, v := range f.Header {
		for index := range v {
			_ = enc.WriteField(qpack.HeaderField{Name: strings.ToLower(k), Value: v[index]})
		}
	}
	quicvarint.Write(&buf, uint64(headerBuf.Len()))
	buf.Write(headerBuf.Bytes())
	return buf.Bytes()
}
